Basic Game Tutorial 4: Character Movement
Back again are we? Good to see you!
In this part of the Basic Game Tutorials, we'll be adding perhaps the most fun part of the project. How to move the character and jump around.
Sounds simple, right? Let's see about that shall we...
Actually, this part will be much more simple than the previous one. We have already completed our collision check and we already have gravity and velocity, so moving the character should be a walk in the park!
Before we actually animate the character, let's just get them moving around properly.
First, we'll tackle the left and right movement. Jumping will be more fun once we can move around a little!
We'll need two if statements to achieve left and right movement, one to check if the left directional button has been pressed, and one for the right directional button.
Before we go ahead and write the if statements, it would be very useful to have a variable to store the character's movement speed. Let's create that first. This will be a global variable at the top of the program. We'll add it at line 7, moving the gravity and velocity variables down a couple of lines:
8. moveSpeed = 5
9.
10. gravity = 1
11. velocity = 0
Done!
Using the Directional Buttons to Move the Character
Before we write the if statements to move the character, we must call the controls()
function to access the Joy-Con buttons!
At the very start of the main loop, add line 34 below. We have included the surrounding lines to make it clear:
31. loop
32. clear()
33.
34. c = controls(0)
35.
36. screenW = gwidth()
37. screenH = gheight()
Great! We now have access to all the Joy-Con buttons by using our c
variable.
Now let's use the moveSpeed
variable to create the movement if statements. We'll begin with a simple version, then we'll add some polish.
Add the following lines to your program:
63. if c.right then
64. playerX += moveSpeed
65. endif
66.
67. if c.left then
68. playerX -= moveSpeed
69. endif
These two very simple if statements do almost everything for us. They allow us to press the left and right directional buttons and increase or decrease the player's x position.
However, run the program and you might notice that if you walk off an edge then quickly walk backwards, you can lodge yourself firmly inside a solid block! This isn't quite what we want.
We need to use our brilliant collision()
function in these if statements to check if the tile we are about to move into is solid or empty.
Make the changes below to your if statements:
63. if c.right and !collide( playerX + tSize / 2 + moveSpeed, playerY + tSize - 1 ) then
64. playerX += moveSpeed
65. endif
66.
67. if c.left and !collide( playerX + tSize / 2 - moveSpeed, playerY + tSize - 1 ) then
68. playerX -= moveSpeed
69. endif
Just like before, we are using the collision
function to check if the position we are about to be in is solid or empty. This is why we must add or subtract moveSpeed
depending on which way we are moving.
The Jump
Programming a jump is a key part of creating a platform game. There are multiple ways to achieve a good jump. Since we are already simulating gravity and velocity, our jump code will be very simple indeed and the results look fantastic!
This system of using gravity and velocity will work in any project you might want to use them in.
All we need to do is adjust the velocity variable. Let's add a very simple if statement to our program:
54. if c.a then
55. velocity -= 2
56. endif
Run the program and press the A button lightly. Hopefully the character will briefly jump into the air. When A is released, we will be brought back down to the ground by gravity. Awesome!
However... This jump is a little strange. We can hold down the A button to constantly move further into the air, which isn't exactly what we'd like! It's a bit silly really.
What we need is a timer which tracks how long we have been in the air, and stops us from holding down the A button to keep flying upwards if the timer reaches a certain point.
First, we'll need to create the variable at the start of our program:
10. gravity = 1
11. velocity = 0
12.
13. jumpTimer = 0
We've shown the gravity
and velocity
variables here to make it clear where to put the jumpTimer
variable.
We begin the timer at 0 and we will count up as we are jumping. Let's add something to the jumping if statement:
55. if c.a and jumpTimer < 12 then
57. jumpTimer += 1
58. velocity -= 2
59. endif
That should do it! Run the program and press the A button to see our newly limited jump.
Oh dear! You might notice that we can only jump once!
This is because when we jump, we are now increasing the jumpTimer
variable and it must be less than 12 if we want to jump again.
In order to jump again, we must reset the jumpTimer
variable when we reach the ground.
In the if statement which checks to see if the character will collide the floor, we must add something after the else
. Here's how the full if statement should look:
63. if !collision( playerX + tSize / 2, playerY + tSize + velocity ) then
64. playerY += velocity
65. else
66. playerY = int( ( playerY + velocity ) / tSize ) * tSize
67. velocity = 0
68. jumpTimer = 0
69. endif
Notice the new line on line 68. We reset the jumpTimer
variable when we know the player has reached the floor.
A Little Extra Polish...
Our jump is pretty much complete. We can only stay in the air for a limited amount of time, we come wonderfully back down to the ground and we collide with the floor. This is really all we need, but it would be quite easy to add a couple of extra things which would really add some polish to our game.
When we jump in real life we have an initial burst of upward velocity, then as we spend more time in the air this decreases until it becomes negative, overcome by the force of gravity. We can make something very similar happen in our jump code with a very simple change:
56. if c.a and jumpTimer < 12 then
57. jumpTimer += 1
58. velocity -= 8 / jumpTimer
59. endif
We have changed line 58 so that our velocity is dependent on the jumpTimer
variable. The longer we have been in the air, the more our velocity is divided by, therefore slowing our jump down as we reach the peak.
To make the jump work nicely we have also increased the amount we modify velocity by to 8. Changing this number will have a big effect on your jump!
You might have noticed that in the middle of your jump, you can press the A button again to pause slightly in the air. This looks a little silly and we can fix it with some very useful code. So let's do it!
We need to keep track of if the A button has been pressed. To do this we'll need a variable. Add the following global variable to the start of the program, just underneath the jumpTimer
variable:
13. jumpTimer = 0
14. oldA = 0
This oldA
variable will only be either true
or false
. We will store the old state of the A button in this variable, and check it against the current state of the A button using an if statement.
Let's put that idea to use:
62. if oldA and !c.a then
63. jumpTimer = 12
64. endif
65.
66. oldA = c.a
This tricky if statement resets the jump timer to the maximum value if the A button is pressed again during the jump.
The Program So Far
As always, here is a copy of the entire program so far to make sure you're up to speed. If you're having problems with your code, feel free to start a new project and copy and paste the entire program to be certain:
1. background = loadImage( "Kenney/backgrounds", false )
2. tilesheet = loadImage( "Kenney/superPlatformPack", false )
3. chrSheet = loadImage( "Kenney/characters", false )
4.
5. playerX = 0
6. playerY = 0
7.
8. moveSpeed = 5
9.
10. gravity = 1
11. velocity = 0
12.
13. jumpTimer = 0
14. oldA = 0
15.
16. screenX = 0
17. screenY = 0
18.
19. tiles = [ 121, 138, 128, 129, 130 ]
20.
21. level = [
22. [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],
23. [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],
24. [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],
25. [ 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
26. [ 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
27. [ 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
28. ]
29.
30. levelHeight = 12
31. levelOffset = levelHeight - len( level )
32. tSize = 0
33.
34. loop
35. clear()
36.
37. c = controls( 0 )
38.
39. screenW = gwidth()
40. screenH = gheight()
41. scale = screenH / ( tileSize( tilesheet, 121 ).y * levelHeight )
42. tSize = scale * tileSize( tilesheet, 121 ).y
43. pSize = tileSize( chrSheet, 96 ) * scale
44.
45. drawImage( background, -screenX, -screenY, screenH / imageSize( background ).y )
46.
47. for row = 0 to len( level ) loop
48. for col = 0 to len( level[0] ) loop
49. if level[row][col] >= 0 then
50. x = col * tSize
51. y = ( row + levelOffset ) * tSize
52. drawSheet( tilesheet, tiles[level[row][col]], x, y, scale )
53. endif
54. repeat
55. repeat
56.
57. if c.a and jumpTimer < 12 then
58. jumpTimer += 1
59. velocity -= 8 / jumpTimer
60. endif
61.
62. if oldA and !c.a then
63. jumpTimer = 12
64. endif
65.
66. oldA = c.a
67.
68. velocity += gravity
69.
70. if !collision( playerX + pSize.x / 2, playerY + pSize.y + velocity ) then
71. playerY += velocity
72. else
73. playerY = int( ( playerY + velocity + pSize.y ) / tSize ) * tSize - pSize.y
74. velocity = 0
75. jumpTimer = 0
76. endif
77.
78. if c.right and !collision( playerX + pSize.x / 2 + moveSpeed, playerY + pSize.y - 1 ) then
79. playerX += moveSpeed
80. endif
81.
82. if c.left and !collision( playerX + pSize.x / 2 - moveSpeed, playerY + pSize.y - 1 ) then
83. playerX -= moveSpeed
84. endif
85.
86. drawSheet( chrSheet, 96, playerX, playerY, scale )
87.
88. update()
89. repeat
90.
91. function collision( x, y )
92. tileX = int( x / tSize )
93. tileY = int( y / tSize ) - levelOffset
94.
95. result = true
96.
97. if tileY < 0 or tileY >= len( level ) or tileX < 0 or tileX >= len( level[0] ) then
98. result = false
99. else
100. if level[tileY][tileX] < 0 then
101. result = false
102. endif
103. endif
104. return result
Functions and Keywords used in this tutorial
clear(), controls(), drawImage(), drawSheet(), else, endIf, for, function, gHeight(), gWidth(), if, int(), len(), loadImage(), loop, repeat, return, tileSize(), then, to, update()